﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace JkUtils
{
    public class JKSysUtil
    {
        /// <summary>
        /// 打开网址（跨平台）
        /// </summary>
        public static void OpenUrl(string url)
        {
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    url = url.Replace("&", "^&");
                    Process.Start(new ProcessStartInfo("cmd", "/c start " + url) { CreateNoWindow = true });
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    Process.Start("open", url);
                }
                else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    Process.Start("xdg-open", url);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("无法打开浏览器: " + ex.Message);
            }
        }

        /// <summary>
        /// 压缩目录到系统临时目录下的 ZIP 文件。
        /// includeRootFolder = true 时：压缩包内部包含根目录(源目录名);
        /// includeRootFolder = false 时：仅包含源目录内的文件与子目录（不出现最外层目录名称）。
        /// 返回生成的 zip 绝对路径。
        /// </summary>
        /// <param name="sourceFolder">源目录</param>
        /// <param name="zipFileName">可选 zip 文件名（不含路径，可含 .zip），为空自动生成</param>
        /// <param name="includeRootFolder">是否在 zip 顶层包含源目录这一层</param>
        public static string ZipFolderToTemp(string sourceFolder, string zipFileName = null, bool includeRootFolder = true)
        {
            if (string.IsNullOrWhiteSpace(sourceFolder))
                throw new ArgumentNullException("sourceFolder");
            if (!Directory.Exists(sourceFolder))
                throw new DirectoryNotFoundException("源目录不存在: " + sourceFolder);

            string baseFolderNormalized = sourceFolder.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
            string rootName = Path.GetFileName(baseFolderNormalized);
            if (string.IsNullOrEmpty(rootName))
                throw new InvalidOperationException("无法确定根目录名称: " + sourceFolder);

            if (string.IsNullOrWhiteSpace(zipFileName))
                zipFileName = rootName + "_" + DateTime.UtcNow.Ticks + ".zip";
            if (!zipFileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
                zipFileName += ".zip";

            string tempDir = Path.GetTempPath();
            string zipPath = Path.Combine(tempDir, zipFileName);

            if (File.Exists(zipPath))
            {
                try { File.Delete(zipPath); } catch { }
            }

            FileStream fs = null;
            ZipArchive archive = null;
            try
            {
                fs = new FileStream(zipPath, FileMode.Create, FileAccess.Write, FileShare.None);
                archive = new ZipArchive(fs, ZipArchiveMode.Create, false, Encoding.UTF8);

                string rootPrefix = includeRootFolder ? (rootName + "/") : string.Empty;

                // 显式创建根目录条目（可选）
                if (includeRootFolder && archive.GetEntry(rootPrefix) == null)
                {
                    archive.CreateEntry(rootPrefix);
                }

                // 目录（包含空目录）
                foreach (string dir in Directory.EnumerateDirectories(sourceFolder, "*", SearchOption.AllDirectories))
                {
                    string relDir = dir.Substring(baseFolderNormalized.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
                    if (string.IsNullOrEmpty(relDir))
                        continue; // 根本身

                    string entryDirName = rootPrefix + relDir.Replace(Path.DirectorySeparatorChar, '/').TrimEnd('/') + "/";
                    if (archive.GetEntry(entryDirName) == null)
                        archive.CreateEntry(entryDirName);
                }

                // 文件
                foreach (string file in Directory.EnumerateFiles(sourceFolder, "*", SearchOption.AllDirectories))
                {
                    string relFile = file.Substring(baseFolderNormalized.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
                    if (string.IsNullOrEmpty(relFile))
                        continue;
                    string entryName = rootPrefix + relFile.Replace(Path.DirectorySeparatorChar, '/');
                    archive.CreateEntryFromFile(file, entryName, CompressionLevel.Optimal);
                }
            }
            catch
            {
                try { archive?.Dispose(); } catch { }
                try { fs?.Dispose(); } catch { }
                try { if (File.Exists(zipPath)) File.Delete(zipPath); } catch { }
                throw;
            }
            finally
            {
                if (archive != null) archive.Dispose();
                else fs?.Dispose();
            }

            return Path.GetFullPath(zipPath);
        }

        /// <summary>
        /// 解压缩：忽略压缩包里的根文件夹（若所有条目共享单一第一段），把其内部内容合并到目标目录。
        /// overwrite=true 覆盖同名文件；false 跳过。
        /// </summary>
        public static ExtractResult ExtractZipIgnoreRoot(string zipPath, string destinationDirectory, bool overwrite)
        {
            if (string.IsNullOrWhiteSpace(zipPath))
                throw new ArgumentNullException("zipPath");
            if (!File.Exists(zipPath))
                throw new FileNotFoundException("压缩文件不存在", zipPath);
            if (string.IsNullOrWhiteSpace(destinationDirectory))
                throw new ArgumentNullException("destinationDirectory");

            Directory.CreateDirectory(destinationDirectory);
            string destFull = Path.GetFullPath(destinationDirectory);

            int filesExtracted = 0;
            int filesSkipped = 0;
            int dirsCreated = 0;

            using (var archive = ZipFile.OpenRead(zipPath))
            {
                string commonRoot = null;
                foreach (var entry in archive.Entries)
                {
                    if (string.IsNullOrEmpty(entry.FullName))
                        continue;
                    string path = entry.FullName.Replace('\\', '/');
                    if (path.Length == 0) continue;
                    string firstSegment;
                    int slashIndex = path.IndexOf('/');
                    firstSegment = slashIndex >= 0 ? path.Substring(0, slashIndex) : path;
                    if (string.IsNullOrEmpty(firstSegment)) continue;
                    if (commonRoot == null)
                        commonRoot = firstSegment;
                    else if (!string.Equals(commonRoot, firstSegment, StringComparison.Ordinal))
                    {
                        commonRoot = null; // 不存在单一根
                        break;
                    }
                }

                foreach (var entry in archive.Entries)
                {
                    string entryName = entry.FullName.Replace('\\', '/');

                    bool isDirectory = entryName.EndsWith("/", StringComparison.Ordinal);

                    string relative;
                    if (!string.IsNullOrEmpty(commonRoot) && entryName.StartsWith(commonRoot + "/", StringComparison.Ordinal))
                        relative = entryName.Substring(commonRoot.Length + 1);
                    else if (!string.IsNullOrEmpty(commonRoot) && string.Equals(entryName.TrimEnd('/'), commonRoot, StringComparison.Ordinal))
                        continue; // 根目录本身
                    else
                        relative = entryName;

                    if (string.IsNullOrEmpty(relative))
                        continue;

                    string targetPath = Path.Combine(destFull, relative.Replace('/', Path.DirectorySeparatorChar));
                    string targetFull = Path.GetFullPath(targetPath);
                    if (!targetFull.StartsWith(destFull, StringComparison.OrdinalIgnoreCase))
                        continue; // ZipSlip 防护

                    if (isDirectory)
                    {
                        if (!Directory.Exists(targetFull))
                        {
                            Directory.CreateDirectory(targetFull);
                            dirsCreated++;
                        }
                        continue;
                    }

                    string parentDir = Path.GetDirectoryName(targetFull);
                    if (!Directory.Exists(parentDir))
                    {
                        Directory.CreateDirectory(parentDir);
                        dirsCreated++;
                    }

                    if (File.Exists(targetFull) && !overwrite)
                    {
                        filesSkipped++;
                        continue;
                    }

                    try
                    {
                        entry.ExtractToFile(targetFull, overwrite);
                        filesExtracted++;
                    }
                    catch
                    {
                        filesSkipped++;
                    }
                }
            }

            return new ExtractResult
            {
                Destination = Path.GetFullPath(destinationDirectory),
                FilesExtracted = filesExtracted,
                FilesSkipped = filesSkipped,
                DirectoriesCreated = dirsCreated,
                Overwrite = overwrite
            };
        }

        public class ExtractResult
        {
            public string Destination { get; set; }
            public int FilesExtracted { get; set; }
            public int FilesSkipped { get; set; }
            public int DirectoriesCreated { get; set; }
            public bool Overwrite { get; set; }
            public override string ToString()
            {
                return string.Format("Destination={0}, Extracted={1}, Skipped={2}, Dirs={3}, Overwrite={4}",
                    Destination, FilesExtracted, FilesSkipped, DirectoriesCreated, Overwrite);
            }
        }
    }
}
